查看原文
其他

不写代码不建模!万字长文带你在 Cocos Creator 中零代码搭建 3D 户外场景

江船长 COCOS 2022-06-10

点击文末【阅读原文】在线预览最终效果。


对于接触过 3D 游戏美术资源的程序来说,可能心中都出现过类似这样的独白:


  • 这些 3D 模型是怎么用的,为什么我导入的时候老是报错?

  • 这些花花绿绿的图片是干什么的?材质是什么东西?

  • 为什么我的场景一片漆黑?为什么场景里的模型没有阴影?为什么场景里的东西一多帧数就暴降?

  • 我在网上购买了美术资源,为什么放进我的场景里就是比人家的卖家秀难看?


与此同时,对于接触过游戏引擎的 3D 美术来说,心里可能正在进行类似这样的独白:


  • 为什么我的模型在引擎里这么 low,渲染图明明高大上的感觉;

  • 我的场景颜色为什么这么难看?我想要的气氛和氛围呢?

  • 为什么程序不让我用更多贴图?他们说帧数低?没贴图出不来效果呀!

  • 为什么即便我把别人的模型放在我的场景里,效果还是没有别人的截图好看?


今天,我们不聊代码,不写 Shader,不做科普。我们将回归起点,以一个从未接触过游戏引擎的纯萌新的视角,在 Cocos Creator 中打造一个 3D 户外场景。这篇文章可能有些长,但是已经看到这里的你,不需要会写代码,不需要会建模雕刻,值需要记得“点这里按那里”即可。


本文将按照基础操作-材质-环境-方向光-动态阴影-其他光源-静态光照-模型的模块化-材质的分配-烘焙的配置-模型的摆放-光照的设计-其他这一脉络展开。那么,我们现在就从基础中的基础开始。


基础操作


当我们打开一个刚创建的崭新 Cocos Creator 项目时,首先看到的界面通常是这个样子的。接下来我们将要进行的所有操作,都可以在资源管理器、场景编辑器、属性检查器层级管理器这四个主要的工作区中展开:



首先,我们可以先把各式各样的美术资源通过资源管理器导入到引擎中。无论是 3D 模型(.fbx, .gltf),图片(.jpg, .png, .tga, .bmp, .hdr, .psd),音频(.mp3, .wav, .ogg,.m4a)还是视频(.mp4)文件,都可以直接拖拽到资源管理器中的 assets 目录下,从而导入到当前的项目中。


资源管理器中的 assets 目录,其实与项目在硬盘上的存储目录下的 /assets 文件夹中的目录结构是完全一样的。因此,在硬盘上项目目录下的 /assets 文件夹进行拷贝和删除的操作,与在资源管理器下进行导入、删除和更新的操作,结果是一样的。

注意:以下格式虽然也很常见,但并不支持直接导入到 Cocos Creator 中:
.obj, .tiff, .gif, .exr, .flac, .mov, .avi, DCC 专属的储存格式如 .max, .ma, .mb, .blend 等。


资源导入后,我们也可以在资源管理器中创建几个新文件夹,把不同类型的资源快速进行分类。已经导入 Cocos Creator 的资源是可以热更新的,换句话说:即便资源已经在场景中使用了,也可以随时更改它在资源管理器中的目录位置;当有需要的时候,可以对某个资源进行修改,然后重新导入资源管理器,所有已经在场景中被使用的该资源的实例也会随着二次导入而更新。



导入的资源如何在场景中使用呢?我们只需要在资源管理器中选择导入的 3D 模型文件,将其拖拽到场景编辑器中即可。你会发现:拖拽到场景中的模型立即出现在层级管理器的列表里,并以绿色显示。这表明模型已经被加入到场景中,成为场景的根层级(默认名为 Scene)的一个子层级。


为什么在我把模型拖拽进场景编辑器中,却看不到模型?这可能是因为模型本身的尺寸比较小。首先在层级管理器中选中模型,按下快捷键 F,可以让场景编辑器自动聚焦到场景中选中的模型上。


保存一下我们的进度吧。按下快捷键 Ctrl/Cmd + S,或者在菜单栏选择 File -> Save Scene,将当前的场景存储在项目目录下的 /assets 文件夹中。你会发现:随着场景作为一个单独的文件存储在项目目录中,场景也作为一个资源出现在资源管理器中,不仅如此,层级管理器中场景根层级也更新为场景文件的命名。



所有的 3D 物体在场景中都可以进行基本的移动、旋转和缩放的操作,这些都可以使用界面顶部左侧的工具栏来实现。



工具栏中的工具与大部分 DCC 的使用方法是相同的。快捷键 W、E、R、T 分别对应移动、旋转、缩放和自由变换工具。选择一款工具之后,使用场景编辑器中相应的变形器对 3D 物体进行位移、旋转、缩放的操作。与此同时,物体的位置、旋转和大小参数也会在属性检查器中相应地更新。在属性检查器中的参数中直接输入数值,也可以完成位移、旋转、缩放的操作。


自由变换工具目前只能在 2D 物体上使用。

在 Cocos Creator 中,参数的数值会自动精确到小数点后三位,因此不会出现浮点数溢出的问题。即便如此,手动输入较为接近整数的数值,仍然是地编的一个好习惯。




除了我们拖拽进场景的模型之外,场景中已经默认自带了一个方向光和一个摄像机。当然,我们也可以对它们进行位移、旋转和缩放的操作。


在对摄像机进行操作时,我们可以在场景编辑器右下角看到一个摄像机视角的预览窗口,帮助我们得到最理想的摄像机角度。



让我们来快速预览一下目前的成果吧。点击场景编辑器上方的 Play on Device 按钮,可以选择在浏览器或模拟器中预览场景运行的效果。


在预览运行中,可以在左下角看到运行时的一些有用的统计数据,包括 FPS, Draw Call, 三角面数量等。在预览窗口的顶部,也可以调整预览的窗口大小、横纵方向等。



对于场景的构建来说,使用浏览器预览就已经够用了。然而这还不能算作真正的 “Play on Device”,如果我需要在手机上预览运行效果呢?


首先,确保手机与我们开发的平台链接同一个 WiFi 网络。在属性检查器上方,我们能看到一串 IP 地址,将鼠标悬浮在 IP 地址上方,会出现一个 QR 条形码。


开发过程中,也可以用手机扫描编辑器右上方的二维码,预览自己的项目。



材质


当前我们导入的模型已经进入了场景里,同时也出现了一个问题:为什么它是绿色的?我们在导出 .fbx 和 .gltf 的时候,不是已经导出材质了吗?


原因其实很简单:无论模型是在哪一款 DCC 制作的,其材质一定是基于那一款 DCC 中的某一个内置 Shader。当今市面上的 DCC 千差万别,Shader 更是不计其数,即便是同样使用 PBR 流程也会分左右手坐标系统以及 Metal/Roughness 和 Specular/Glossiness 流程的区别。所以将一个 3D 材质无缝从 DCC 中直接迁移到任意一款引擎里都是比较难做到的。当我们导出 3D 资源时,实际上只是导出了模型的 Material ID 和基于 Material ID 自动赋予的默认材质文件。在引擎中,我们需要根据模型的 Material ID,重新赋予基于引擎内 Shader 的新材质。


换句话说:模型的 Material ID 才是赋予材质的关键。在 DCC 制作模型的过程中,只要定义了 Material ID,确保没有漏掉没有被定义 Material ID 的网格,即便在没有赋予材质的情况下导出模型也没有关系。


层级管理器中展开模型的层级,我们会发现模型的网格数据作为子层级,使用 cc.MeshRenderer 模组存储在 3D 模型的 prefab 当中。在 cc.MeshRenderer 模块下有 Materials 数组参数,并且已经包含了一个 .material 文件。



这个 .material 文件就是我们模型呈绿色的原因。这个 .material 文件是随模型的导出创建的,它在 DCC 中的渲染功能并不能继承到 Cocos Creator 中。Materials 参数是一个数组,因为一个网格可以有多个 Material ID,即便网格导入时自带的材质不能正确渲染,但只要网格的 Material ID 数据不变,我们只需要用 Cocos Creator 内置的材质,替代 Materials 参数中的材质即可。


在上图中,Materials 参数只包含一个 .material 文件,说明这整个网格只有一个 Material ID,只能使用一个引擎的材质进行替换。注意:Material ID 与网格面是一一对应的关系,虽然在 Cocos Creator 中可以手动修改 Materials 参数从而改变 Material ID 的数量,但没有定义相应的网格,这种修改不会有任何效果。对 Material ID 的修改,一定要在 DCC 中进行。


下面,我们只需要把 .material 文件,替换成 Cocos Creator 中的材质文件即可。


首先我们在资源管理器中点击左上方的加号按钮,或在资源管理器的任意空白区域右击,选择 Create -> Material,新建一个空的材质文件。


注意:Cocos Creator 中的材质文件是 .mtl 格式,不是 .material。


选取新建的材质,在属性检查器中可以看到当前材质的各项参数。首先在 Effect 参数的下拉菜单中选择该材质所使用的 Shader。



新建的材质默认使用 Cocos Creator 内置的 PBR Shader。它的使用方法非常简单:只需要勾选法线、Albedo 和 PBR 贴图的选项,将贴图赋予相应的参数即可。


除此之外,我们还需要勾选 USE LIGHTMAPUSE SECOND UV,虽然这两个参数与材质的效果无关,但它们的重要性,后面会体现出来。


Cocos Creator 默认 PBR Shader 中的 PBR 贴图,指的是 Metal/Roughness 流程下的 AO, Roughness, Metallic 贴图分别以红、绿、蓝通道存贮在一张贴图里。关于更多详细内容和该 Shader 的使用方法,请参考相关文档。


Shader 参数的调节完成后,点击属性检查器右上角的绿色对号按钮,保存对材质的修改。之后,回到层级管理器中模型层级的网格,将新的材质赋予 Materials 参数,替换已有的 .material 文件,材质的赋予就完成了。


在我们结束对模型的处理之前,先将 LightmapSettings 标签下的 BakeableCastShadowReceiveShadow 选项勾选,并将 ShadowCastingMode 设为 ON,这样操作的目的,我们在稍后会详说。



环境


我们已经成功将模型导入了 Cocos Creator 的场景里,并使用 Cocos Creator 的 Shader 达成了材质效果的表现,然而这仍然是不够的。毕竟:一个孤零零的 3D 模型矗立在场景里,周围是无穷无尽的真空,这与我们正常理解中的场景相差万里。所以,我们需要为场景准备一个较为自然的环境,作为一个宏观的背景。


当然,我们没有必要把背景中的所有远山近水日月星辰全部用模型实现一遍:这样的规划并不科学,而且背景中的景象也并不需要复杂的呈现。所以,使用环境全景贴图就成为了最佳选择。


首先我们将全景贴图(.hdr)通过资源管理器导入到引擎中,在资源管理器中选中导入的资源,可以在属性检查器中查看相应的属性参数。全景贴图因其 360 度的投射方式,需要将 Type 参数设为 texture cube,另外,由于 .hdr 格式的颜色数据使用线性颜色空间存储,需要开启 Is RGBE 参数。在一般情况下,这些设置在贴图导入的时候已经自动完成了。


那么,如何使用全景贴图呢?在层级管理器中选择场景的根层级,属性检查器会相应地显示场景的环境属性参数。在 Skybox 标签下将全景贴图资源拖拽到 Envmap 参数上,开启 Enabled



启用了环境贴图后,你会发现 Ambient 标签下的 SkyLightingColorGroundLightingColor 发生了变化。这两个参数分别代表天空漫反射颜色和地面反射颜色。所谓天空漫反射,指的是空气中的微粒和尘埃通过反射太阳光,间接对环境产生照明效果的颜色,美术上可以简单理解为白天户外没有阳光时,物体受光的明部应该呈现的颜色。地面反射是指环境中的光照在地面上发生反射之后所表现的颜色,美术上可以简单理解为环境中的物体暗部应该呈现的颜色。所以,这两个参数的关系,有点类似调色中高调和低调的关系,它们的配合将决定物体受光的整体颜色表现。


引擎会根据环境贴图的明度自动选取天空漫反射颜色和地面反射颜色。然而这种选择未必是我们需要的。使用这两个参数最简单的方法是对照环境贴图中的颜色:将 SkyLightingColor 调整为与贴图中天空颜色相近的颜色,将 GroundLightingColor 调整为与地面颜色相近的颜色。中间的第三个参数 SkyIllum 控制全景贴图的曝光度,同时也影响 SkyLightingColorGroundLightingColor 的强度。这个参数没有标准的数值,我们完全可以按照自己的需求决定。这是因为 .hdr 贴图是包含全局的曝光范围数据的,不会出现普通图片的曝光不足或过度的问题,这当然也是它需要以线性颜色空间存储的原因。


在结束对环境的处理之前,我们还需要打开属性检查器中的 Shadows 标签,勾选 EnabledType 选取为 ShadowMapPcf 选取为 SOFT_2XShadowMapSize 选取为最大值(Ultra_2048x2048)。这样操作的目的,我们会在下面揭晓。


最后,为了在运行时渲染环境贴图,我们还需要在层级管理器中选择摄像机,在 cc_Camera 模块下的 clearFlags 参数中选择 SKYBOX。否则,我们的环境贴图只会在编辑器中可见。


方向光


仅靠天空漫反射和地面反射,显然是远不够为整个场景提供照明的。我们还需要在场景中建立特定的光源来实现充足的流明。


Cocos Creator 中新建的场景已经默认包含了一个方向光作为整个场景的主光源。我们也可以在层级管理器中点击左上角的加号按钮,或在层级管理器中任意空白的部分右击选择 Create -> Light,在场景中新建一个灯光。



方向光会依照一个方向,无差别地对整个场景进行统一的照明,它比较类似现实中的太阳光。在编写 Shader 的过程中,我们可以使用内置参数 cc_mainLitDir 来获得主光源的相关参数数据。


诚然,现实生活中的光照效果并不是一个方向光单独实现的,依据场景的不同也不一定会出现需要方向光特性光源的需求。即便如此,在场景中保留一个方向光作为主光源仍然是一个很好的选择。这不仅是因为 Shader 可以通过一个内置参数方便地访问光照信息,光照的计算通常会带来较大的性能负担,使用一个单独的光源来指代场景中所有光照的综合,可以减轻光照计算的数量,避免不必要的性能问题。

在同一场景中可以开启多个方向光,但同时只会有一个方向光发生作用。


方向光的参数非常简单,它的旋转参数即代表了方向光照射的方向,除此之外它还有光源的颜色参数、强度参数和色温参数。


色温是一种用热量单位 K(开尔文)衡量光照颜色的度量方式。简单而言:色温越高,光的颜色越偏蓝(越接近“冷”色调);反之,色温越低,光的颜色越偏红(越接近“暖”色调)。当色温大概处于 6500K 左右的数值范围时,光的颜色是我们熟悉的偏白色调。因此,我们不需要对色温参数给予太多关注,可以直接通过颜色参数决定光源的颜色变化。


另外,方向光的位置参数是不会对光照效果有任何影响的。方向光是一种遍布整个场景的光源,只要方向和强度不变,方向光的光源在场景的任何位置都不会造成光照效果的变化。



动态阴影


光源已经有了,下面我们需要的是阴影。


在场景中开启动态阴影,需要对环境、模型和光源三者进行分别设置:


  • 首先是模型,在层级管理器中选择模型网格所在的层级,在 LightmapSettings 标签下,将 ShadowCastingMode 设为 ON

  • 其次是光源,选取光源,在 StaticSettings 标签下勾选 CastShadow

  • 最后是环境,在层级管理器中选择场景的根层级,在 Shadows 标签下勾选 Enabled


Cocos Creator 中有两种动态阴影类型,可在 Shadows 标签的 Type 参数中选择。Planar 是一种简单的阴影类型,它只能在投射在正对 x, y 和 z 轴的方向上。如果接受阴影的物体凹凸不平,或者呈一定角度的旋转的话,则阴影会穿模而过。与之相关的设置包括决定阴影颜色的 ShadowColor 和决定投射平面方向的 Normal 参数。


ShadowMap 是一种更多细节的阴影类型,它可以投射到任何表面的模型上。获得理想的阴影效果需要多个参数的共同配合:


  • 首先调整 ShadowMapSize,确保有足够的像素数量绘制阴影。如果在场景中看不到阴影,或者阴影呈若干矩形状,那么这是首先需要调整的参数;

  • 其次,将 Pcf 选取为 SOFT_2X, Shadow Map 默认会形成锯齿状的边缘,调整 Pcf 的设置可以开启相关模糊算法,柔滑过于锋利的阴影边缘;

注意:Pcf 仅对生成的 Shadow Map 进行像素处理,无法达到范围光源的半影效果。

  • 调整 ShadowDistance 的数值,如果阴影细节过低或过于模糊,可以降低 ShadowDistance 的数值。这一调整与增高 ShadowMapSize 参数的效果是类似的,两者可以配合调节:如果通过调整 ShadowDistance 可以得到满意的效果,可以相应降低 ShadowMapSize 以节省性能;

注意:当 ShadowDistance 等于 0 时,将不会产生阴影。

  • 最后,调整 Saturation 的数值,这相当于阴影的半透明度。



其他光源


除了方向光之外,我们还可以在场景中加入球形灯和聚光灯。



球形灯是一种光源为球形,向四周无差别照明的光源。球形灯是一种“放在哪里亮哪里”的光源,与方向光相反,球形灯的旋转参数不会对光照产生任何影响,也不能无差别照亮整个场景,它只能作用于有限的范围,因此位置参数是使用球形灯制作光照的主要因素。球形灯同样有颜色、色温和强度的参数。除此之外,球形灯还有球形的大小(Size)参数,和控制光照半径的范围(Range)参数。


大小和范围,都是球形的半径,有什么不同呢?我们可以把球形灯想象成一种外壳发亮照亮周遭环境,但内核是空心的球体,Size 参数决定了这个球体的半径大小。随着 Size 参数增大,整个球体作为光源发光的强度也会增大。而 Range 决定了光照能够触及的范围,范围的增大并不会改变光源的强度。



聚光灯的光源也是一个球体,但它并不向四周无差别发射光线。聚光灯在一个圆锥形范围内照亮环境,相应地会留下圆形的高亮范围。现实生活中,聚光灯是舞台和摄影中最常用的灯光。聚光灯的位置参数决定了光源位置,旋转参数决定了锥形照射范围的朝向,它同样有颜色、色温、强度、光源大小和光照范围的参数,并且可以调节锥形照射范围的角度(SpotAngle)。


方向光、球形灯和聚光灯的组合使用,将帮助我们实现细节的光照效果。然而目前版本的 Cocos Creator 中,球形灯和聚光灯都是不支持动态阴影的。但这并不意味着我们只能使用方向光实现阴影的效果。


静态光照



在任何一款游戏引擎中,动态光照都会带来较大的性能开支。一个灯光就可以导致 Draw Call 数量的翻倍。而完整为一个场景完成照明,我们通常需要几十甚至上百个灯光,这种性能损耗显然是不能接受的。因此,将光照产生的明暗关系烘培为贴图就成为了主流游戏引擎都会选择的做法。这样既可以得到灯光产生的细节光影效果,同时省去了复杂的光照计算,极大提升了运行的效率。然而,随之而来的缺点是灯光不再有热更新的功能,如果需要对已经烘培好的光照效果进行修改,必须进行重新烘培。


在 Cocos Creator 中烘培静态光照之前,我们需要满足一个条件:场景中使用的所有模型必须有两套 UV,其中第二套 UV 必须限制在 UDIM1001 中,并且不能有 UV 重叠。


做出这样限制的原因也很明显:我们将用贴图的方式将灯光的明暗关系储存下来,而贴图的运用需要依赖模型 UV 摆放和使用率。用于绘制材质的 UV 允许有 UV 重叠的情况,毕竟模型的材质某些部分可以看上去较为类似,重叠 UV 可以提高 UV 的利用率,增加贴图精度。而对于光照而言,这种重叠的可能性几乎不存在。然而,如果仅仅是为了烘培光照,就要求材质一律使用不可重叠且限制在 UDIM1001 的 UV,又束缚了材质的制作方式和呈现效果。所以,最理想的办法是准备两套 UV,其中第一套(UV0)主要用于材质,第二套(UV1)则用于烘培光照。


这也是主流引擎包括 Unity 和 Unreal Engine 对静态光照的解决方法,当然 Unity 和 Unreal Engine 都提供某种第二套 UV 的自动解决方案,然而对于美术要求比较高的项目来说,美术对第二套 UV 的手动处理几乎是必须的。


在 Cocos Creator 中,烘培静态光照需要对环境、模型和光源和材质四者进行分别设置:


  • 首先是模型,在层级管理器中选择模型网格所在的层级,在 LightmapSettings 标签下,将 ShadowCastingMode 设为 ON,并且勾选 Bakeable, CastShadowReceiveShadow

  1. ShadowCastingMode 的作用是生成阴影,如果不予设置,该模型将不会产生投影;

  2. Bakeable 的作用是允许该物体加入烘培的队列,如果不予设置,该模型将继续使用动态光照;

  3. CastShadow 是模型是否投射阴影的开关;

  4. ReceiveShadow 是模型是否允许其他模型的投影投射到自己身上的开关;

  5. lightmapSize 决定了该模型烘培出的贴图大小,关于该参数的数值,我们会在后文加以展开;

  • 其次是光源,选取任意光源,在 StaticSettings 标签下勾选 BakeableCastShadow

  • 然后是环境,在层级管理器中选择场景的根层级,在 Shadows 标签下勾选 Enabled

  • 最后是材质,在资源管理器中选取在场景中使用的材质,点选 USE LIGHTMAPUSE SECOND UV


你会发现:烘培静态光照于动态阴影的设置有一些重叠。当然,在没有开启阴影的情况下,烘培静态光照仍然是可行的。


设置完成之后,在编辑器顶部的菜单栏选择 Project -> Lightmap,打开烘培器窗口。



这里的绝大多数参数都不需要我们修改。我们需要注意的是:


  • MSAA:这将影响烘培结果的精度,同时会明显增加烘培所需要的计算时间,提高该数值可以解决烘培结果模糊、有脏污等质量问题;

  • Resolution:顾名思义,这决定了烘培出贴图的分辨率;

  • AOColor:默认的颜色太浅,烘培的结果可能不够明显。初次烘培的时候可以直接选择纯黑色,随后根据调整的需求选择不同的颜色。


设置完成后,点击 Lightmap Generate,在弹出的对话框中选择烘培输出的路径(默认的项目目录 /assets 文件夹即可),开始烘培。



当烘培器窗口下方的状态栏提示 Build lighting 100% 时,说明烘培完成了,我们可以通过 Baked 标签栏查看烘培结果的预览。烘培出的贴图依照之前的设置,应该存储在项目目录 /assets/LightFX/output 中。


我们可以看到,烘培产出了两张贴图,分辨率都是 1024 × 1024(与我们在烘培器中 Resolution 的设置相同),贴图如预览所示。


为什么烘培出的贴图是这个样子的呢?我们需要回到模型去看。



目前场景中有 6 个单独的模型,其中闹钟的主体由 5 个模型组成:钟身、时针、分针、秒针和玻璃挡板。除了玻璃挡板之外,其他 4 个模型都开启了静态光照烘培,而且它们的 LightmapSize 都设为了 512。地面由一个单独的面片构成,它的 LightmapSize 设为了 1024。


为什么玻璃挡板没有开启烘培?因为烘培器无法分辨材质的不透明度,玻璃挡板的模型覆盖了整个钟面,如果开启烘培,玻璃挡板投射的阴影会把整个钟面覆盖。同理,所有使用半透明、全透明材质的模型,都不宜开启阴影和烘培。


对比我们在烘培器中 Resolution 的数值 1024,我们会发现:烘培器会依照模型的 LightmapSize 数值大小,将烘培出来的静态光照绘制到一张分辨率为烘培器 Resolution 数值的贴图上。如果模型的 LightmapSize 与烘培器的 Resolution 正好相等,那么烘培器会为这个模型单独烘培一张贴图;如果模型的 LightmapSize 比烘培器的 Resolution 小,那么该模型烘培出的静态光照会只占到一张贴图的一部分,剩下的部分会预留给其他模型的静态光照贴图。


在这个例子中,模型的 LightmapSize 是 512,是烘培器 Resolution 的一半,一张 1024 × 1024 的贴图,恰好能容下 4 张 512 × 512 的贴图。所以,钟身部分的静态光照恰好占到了一张烘培出贴图的 1/4。贴图中的光影关系,是依据场景中的光照,按照模型的第二套 UV 绘制的。至于剩下的 3/4,当然对应的是时针、分针和秒针的烘培结果。而它们为什么是黑色的,只因为它们本身尺寸较小,又处在下凹的位置,所以没有复杂的明暗关系表现,静态光照并不明显。地面的 LightmapSize 与烘培器的 Resolution 数值相同,所以烘培器专门为它烘培了一张静态光照贴图。


由此,我们可以总结出:模型的 LightmapSize 参数,一定要比烘培器的 Resolution 参数小,并且最好是 POW 数值(64,128,256,512等),这样能在保证烘培效果的同时,最大程度利用每张静态光照贴图的空间。


注意:在目前版本的 Cocos Creator 中,一个场景的静态光照贴图总数不能超过 8 张。

注意:光源的颜色可以被烘培到静态光照贴图当中,但色温效果不能。


烘培完成了之后,我们要如何看到效果呢?材质中的 USE LIGHTMAPUSE SECOND UV 在这里起到了作用。当 USE LIGHTMAP 开启时,材质会将烘培的静态光照贴图叠加到材质的输出上。反之,当 USE LIGHTMAP 关闭时,烘培的静态光照贴图不会被使用,材质仍然会使用动态光照。


当我们不再需要静态光照时,除了将材质的 USE LIGHTMAPUSE SECOND UV 关闭,我们还可以点击烘培器中的 Lightmap clear,或者删除项目目录中 /assets/LightFX 的数据清除当前的烘培信息,让场景回归使用动态光照的状态。


介绍到这里,我们已经熟悉了在 Cocos Creator 中构建 3D 场景的基本知识,那么我们在构建一个场景时,又有什么技巧呢?


模型的模块化



我们在游戏中经常看到的场景 3D 资源,通常是以模块化的方式制作的。所谓模块化,简而言之就是把场景中经常会出现的部分分割成单独的小块,以独立的资源导入引擎中,然后在引擎中完成各个模块的拼接,并为每个模块赋予独立的材质。所以,场景中庞大的建筑物,其实是由多个单独的墙面模块拼接而成的。这种工作流程除了可以避免重复工时,更可以有效避免贴图精度的问题。


我们知道,一个 3D 模型的材质表现,依赖于它的 UV 和使用贴图的分辨率。如果 UV 的使用率过低和 / 或贴图的分辨率过低,我们在近距离观察模型时就能看到图片像素的痕迹,从而大大降低渲染的质量。然而,贴图的大小毕竟是有限的,游戏场景对贴图的压缩程度本来就比较高,如果是一个在场景中尺寸非常大的模型,近距离观察的情况难免占到绝大多数,这种问题就变得更加难以避免。所以,我们会把原本较大的建筑物,切成若干可以重复使用的模块,各个模块各自使用自己的 UV,使一张贴图的精度可以得到最大程度利用。


反之,对于一些尺寸较小的模型,比如灯笼、井盖、垃圾桶等,我们可以把它们的 UV 摆放到同一个 UDIM 空间中,让它们共享一张贴图的资源量。


当然,模块化也有它的缺点:由于不能直接在模型上做细节,同一个模块重复的次数太多就很容易被看出来,引发视觉疲劳。我们可以在制作模块的时候多做几种同样类型但不同细节的变种,以及在引擎中拼接时有意识地添加其他模块点缀来打破这种重复感。


除了第一套 UV,为了进行静态光照的烘培,我们还需要为模型准备第二套 UV。我们已经总结出,模型的静态光照依照它的第二套 UV 烘培并占用一整张静态光照贴图的一部分,因此第二套 UV 的使用率,决定了它烘培的静态光照贴图的精度,我们应当尽可能地提高 UDIM 的利用率,换言之:我们要尽可能更充分地利用它在烘培的静态光照贴图中所占的那一部分。


除此之外,在制作需要拼接在一起(如墙面、地面等)的模型时,我们最好将他们的大小设为整数值,这样操作有什么好处,我们会稍后揭晓。



总结起来,我们在准备模型时,应该注意的是:


  • 尽量将整个场景的模型,分割成较小的、可以复用的模块;

  • 对于在场景中尺寸较大的模型,为它准备一整张贴图的预算,将其第一套 UV 在 UDIM 空间中充分利用展开;

  • 对于在场景中尺寸较小的模型,让它与其他同样尺寸较小的模型共用一张贴图的预算,让它们的第一套 UV 共享同一个 UDIM 空间;

  • 无论尺寸大小,每个模块的第二套 UV 都应该尽可能充分利用 UDIM 空间;

  • 墙面、地面等需要拼接的模块的大小应为整数值。


模型准备好之后,相应地给予 Material ID,就可以导出模型文件和导入引擎了。


材质的分配


资源管理器中导入模型文件后,我们下一个目标是把不同的模块依照场景的设计重新拼接起来。在此之前,我们需要先确定每一个模块的材质。


前面我们提到了,每一个独立的模块应该有其相应的独立的材质。但是对于尺寸较小的模块来说,它们本来就共用一套贴图,有没有可能它们也可以共用一个材质,从而提升运行性能呢?


在这里就不得不提到材质的另外两个功能:Instancing 和 Batching。



开启 Instancing 和 Batching 非常简单:在资源管理器中选取材质资源,在属性检查器中勾选 USE INSTANCINGUSE BATCHING 即可。


注意:Instancing 和 Batching 不能同时开启。


那么,Instancing 和 Batching 有什么作用呢?简单来说:Instancing 能够使场景中所有使用同样模型同样材质的节点合并到同一个批次渲染,Batching 能够使场景中所有使用同样材质但不同模型的节点合并到同一批次渲染。


这样解释恐怕仍然有点抽象。我们在模型准备阶段已经将场景分割为可复用的模块,比如一栋建筑物的墙面,就可以由同一个模型,使用同一个材质,复制出若干份拼接到一起来构建。既然这些墙面节点使用同样的模型,应用同样的材质,只是被复制出来了许多份实例,在渲染时完全可以归入一个批次一次性全部完成。Instancing 就起到了这样的作用:我们只需要在墙面的材质中,勾选 USE INSTANCING,只要所有使用这个墙面材质的都是同样的模型,无论它被复制出多少个实例,都会被归为一个批次渲染,从而明显地增加渲染的效率。


Batching 的情况则稍微复杂一些。在同一个场景中,我们通常可以使用四方连续贴图,制作可以灵活应用在不同 UV 模型上的材质。比如我们需要制作一个玻璃窗的材质,由于场景中所有的玻璃窗差别不大,我们完全可以只制作一个材质,然后把它应用到不同形状、结构、大小、UV 的玻璃窗模型上。那么,这些共享一个玻璃材质的节点能否放在同一个批次渲染呢?虽然它们共用同一个材质,但是它们的模型各不相同,因此 Instancing 是无法使用的。这时,我们可以勾选 USE BATCHING


除此之外,我们还需要把所有使用了开启 Batching 材质的节点,在层级管理器中排列在一起。关于使用 Batching 的详细说明,可以参考官方文档:

https://docs.cocos.com/creator/manual/zh/ui-system/components/engine/ui-batch.html


烘焙的配置


调整好材质,我们再回到模型的 cc.MeshRenderer 模块,为烘培准备相关设定。


前面我们提到过:在同一个场景中,静态光照贴图的总数不能超过 8 张,这看上去好像并不多。所幸的时,静态光照贴图并不需要太高的贴图精度。光照贴图通常是一个明暗关系的渐变,渐变的精度并不会受到贴图分辨率的影响。只要我们的第二套 UV 得到了充分的利用,我们不需要为 lightmapSize 提供过高的数值。依照模型在场景中的大小比例,我推荐的 lightmapSize 数值是:



经过首次烘培之后,根据烘培的结果,我们可以随时在场景编辑器中点选任意模型(这将直接在属性检查器中显示其 cc.MeshRenderer 模块)对模型的 lightmapSize 参数进行单独设置。比如:完全处于明面或暗面的模型不需要太多光照细节,可以取较小的值;如果模型上投射了其他模型的阴影,为了增加阴影的细节,可以取较大的值,等等。


模型的摆放



表面上看,模型的摆放是一项挺轻松的工作:我们只需要把模型从资源管理器拖拽到场景编辑器当中,赋予材质,调整位置、大小、旋转等参数,将需要拼接的模块拼接到一起,然后复制(Ctrl/Cmd + D)出更多实例重复以上操作即可。


虽然简单,模型的摆放直接决定了场景最后的结构呈现,仍然需要我们对效果保持把控,并在需要的时候应用一定的创意:


  • 摆放的模型,尤其是需要拼接在一起的模型,不能相互之间留有缝隙。在场景编辑器中可能看上去并不明显,但一旦烘培静态光照,这些缝隙通常会投射极细的亮边。那么,我们怎么能够有效保证拼接在一起的模型没有缝隙呢?还记得我们在制作模型时要求模型的大小是整数值吗?我们只要在摆放第一个模块时将其放在位置参数为整数的位置,后续的模块只要在位置参数中输入第一模块的位置 + 模块大小的倍数,就能非常精确地保证后续模块的绝对无缝衔接。最后,把拼接好的模型打成一组(建立一个新节点,在层级管理器中将所有模型拖拽到新节点的子层级下),就可以无视位置参数是否为整数,自由调整位置了。

  • 摆放的模型不能千篇一律地使用默认的旋转参数,毕竟现实中不太可能看到几十个整整齐齐正面完美朝向你的垃圾桶。

  • 面对几十个甚至上百个模块的模型和空空如也的场景编辑器,你可能一时不知道该从哪里下手。这时我们可以套用美术上常用的“从大到小”原则:先从“一级结构”下手,这主要指代尺寸最大最明显的结构,比如地形、马路、建筑等;随后过度到“二级结构”,比如门窗等;之后到“三级结构”,交通灯、垃圾桶等;最后到“细节”,如被丢弃的酒瓶、叠在一起的旧纸箱等。使用这种方法的最大好处是可以保证整体的完成度:避免出现一个区域堆满细小物件,另一个区域空空荡荡的情况。


光照的设计


现在我们的场景中已经有了方向光、天空漫反射和地面反射。把它们的颜色和方向参数依照环境全景贴图进行调节,已经能让场景中的模型较好地与背景融合了。接下来我们需要使用球形灯和聚光灯,对场景的光照进行一些具体的设计。在此过程中,我们希望达到的效果是:


  • 真实感,我们从逻辑常识判断中会对光影关系有一定的预期,我们需要用视觉的方式还原这种预期;

  • 丰富感,美术上的俗话说:“能用渐变的,就不要用单色”,我们希望在场景中看到更多光照颜色和强度上的变化;

  • 体验感,虽然我们在追求美术的效果,但我们的产品还是需要面向受众的,因此我们需要对场景整体的体验做一些把控,避免出现一些过于明亮耀眼或伸手不见五指的区域出现。


目标听上去很美好,那么我们怎么使用球形灯和聚光灯实现呢?


首先我们可以依据模型所表达的物件,依据合理性为其增加相应的光源,如路灯、交通灯、室内照明灯等。通常境况下,聚光灯是一个理想的选择,因为它有清晰的圆形照亮范围,而且非常容易产生投影,配合模型使用非常容易增加光源的真实性。一面单调的白墙看上去比较寡淡,但如果有一个狭长的投影投射在上方,会增加不少趣味性。


注意:Cocos Creator 目前不支持使用 IES 文件改变光源形状。



球形灯则是增加“渐变”的极佳工具:当我们不考虑光照的来源,只希望在某块区域增添明度和颜色的变化时,球形灯通常是第一选择。比如:某片区域过暗,需要一定程度的提亮,且不能破坏它整体处于暗部的效果,我们可以放入一个球形灯并给它一个偏冷、偏暗的颜色,既能避免一片漆黑,又能增加一些明度的渐变变化,颜色的变化更有助于烘托氛围;又比如:房间一角有若干尺寸较小的顶光,它们共同照亮这个角落使其呈现某种颜色,要制作这种效果,我们不需要为所有顶光的模型一一附上光源,只需要一个或若干个球形灯,放置在这个角落的位置上,就能以制作出整个角落的空间被笼罩在光照中的效果。


另外,球形灯也可以配合模型使用,比如:我们需要制作一个灯笼向四周发散的灯光,如果将光源放置在灯笼模型中心,模型会遮挡光照,如果把光源放置在模型之外,模型上靠光源太近的一面会受光过度。这时我们只需要调整球形灯的大小(Size参数),使它包裹整个灯笼模型,我们就可以在灯笼模型本身不受球形灯影响的情况下,用球形灯照亮灯笼四周。



其他


模型和灯光现在已经都就位了,我们还有其他手法增强场景的表现力吗?在 Cocos Creator 中,我们还可以使用其他的手段为场景增色。


雾效


雾效的开启非常简单。在层级管理器选择根层级,随后在 Fog 标签下开启 Enabled。Cocos Creator 中的雾效包括普通雾和层级雾,可以在 Type 参数中进行切换。开启 Accurate,可以从体素级别的雾效渲染,提升到像素级别,以牺牲一定性能的前提下,获得更细腻的效果。



普通雾即是我们熟悉的雾效:近景可见,远景逐渐被一层半透明的渐变覆盖。Cocos Creator 提供 LINEAREXPEXP_SQUARED 三种类别的普通雾。这三者的区别在于渐变曲线的区别(直线和指数曲线),EXP_SQUARED 是最接近现实的渐变曲线。层级雾则是以地平面为起点,覆盖一定高度的雾效。



Billboard


Billboard 是一种 2D 图片的渲染方式,它渲染的 2D 图片永远正对摄像机方向。在 3D 技术尚不成熟的年代,Billboard 通常用于将 2D 的 Sprite 渲染到 3D 环境中(俗称 2.5D)。利用 Billboard 永远正对摄象机的特性,我们可以使用一张渐变贴图,快速做出体积光的假象。


Billboard 的使用非常简单。在层级管理器中点击左上角的加号按钮,或在任意空白的区域右击,选择 Create -> Empty Node。随后,点选新创建的空节点,在属性检查器末尾点击 Add Component,选择 Effect -> Billboard。在 cc.Billboard 模块中,赋予一张贴图,使用 HeightWidth 参数调整以下大小,将节点移动到场景中合适的位置即可。


粒子


无论是雾效还是 Billboard,它们都只能使用颜色和亮度的变化烘托氛围,制作环境特性的假象。当我们需要较为复杂和具体的环境效果时,就需要使用粒子系统了。关于粒子系统,我们会在下一篇文章中,进行详细介绍。




点击文末【阅读原文】在线预览本文搭建的户外场景最终效果,未来我们将为大家带来更多 3D 渲染相关内容,敬请关注!


往期精彩

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存